#pragma once

#include <iostream>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <sstream>
#include <sys/wait.h>
#include <unistd.h>
#include <pthread.h>
#include <string>
#include <sys/epoll.h>
#include <unordered_map>

#include "util.hpp"

namespace http_server{
using std::string;
using std::unordered_map;
using std::stringstream;
using std::cout;
using std::endl;

typedef unordered_map<string,string> Header;//用unordered_map来存放键值对

struct Request
{               //请求对象结构体

/****************首部***********************************/
    string method;//方法
    string url;//url
    //例如url为一个例如 http://www.baidu.com/index.html?kwd="cpp"
    string url_path;         //路径，index.html
    string query_string;     //键值对的参数，kwd="cpp"
    //string version;        //暂时先不考虑版本号
/****************Header*****************************/    
    Header header;                //请求报文中的header 
/****************body*******************************/    
    string body;             //请求body
};

struct Response
{           
/*****************首部******************************/
    int code;                     //状态码
    string desc;             //状态码描述
    //string version;        //暂时不考虑版本号

    //下面这两个变量专门给处理静态页面时使用的
    //当前请求如果是请求静态页面，这两个字段会被填充
    //并且cgi_resp为空
    Header header;                //相应报文中的header 
    string body;             //响应body

    //下面这个变量专门给CGI来使用
    //并且当前请求是CGI的话，cgi_resp就会被CGI程序来填充
    //并且，header和body这两个字段为空
    string cgi_resp;         //CGI程序返回给父进程的内容
                                  //包含了部分header和body
                                  //引入这个变量，是为了避免解析CGI程序返回的内容
                                  //因为这部分内容可以直接写回道socket中
};

//当前请求的上下文，包含了这次请求的所有需要的中间数据
//方便进行扩展，整个处理请求的过程中，每一个环节都能够拿到所有和这次请求相关的数据
//这是一个上下文对象，为了个下面的几个函数实现参数统一，方便，万一其中哪个函数需要添加参数了
//直接在这个上下文对象中添加就可以了
class HttpServer;//前置声明，为了下面的server可以用
struct Context
{
    Request req;
    Response resp;
    int new_sock;
    HttpServer* server;//这个成员为了用来访问Httpserver的非静态成员
    int epoll_fd;
    epoll_event ev;
};

//实现核心流程的类
class HttpServer
{
public:
    //以下的几个函数，返回0表示成功，否则表示失败
    //服务器初始化函数
    int ServerInit(const string& ip,short port);

    //下面两个函数在用到时不是通过对象调用的
    //因为那个时候还没有server对象，所以要调用必须设为static
    
    static void ProcessConnect(int listen_sock,int epoll_fd);  //建立连接函数
    //线程入口函数
    static void* ThreadEntry(void* arg);
    //打印请求的测试函数
    static void PrintRequest(const Request& req);
private:
    //从socket中读取一个字符串，输出Resquest对象
    int ReadOneRequest(Context* context);
    //根据Response对象，拼接成一个字符串，写回到客户端
    int WriteOneResponse(Context* context);
    //根据Request对象，构造Response对象
    int HandlerRequest(Context* context);
    //构建404Response对象
    int Process404(Context* context);
    //解析首行
    int ParseFirstLine(const string& first_line, string& method, string& url);
    //解析Header
    int ParseHeader(const string& header_line,Header& header);
    //解析Url
    int ParseUrl(string& url,string& url_path,string& query_string);
    //处理静态文件的函数
    int ProcessStaticFile(Context* context);
    //获取文件路径函数
    void GetFilePath(const string& url_path,string& file_path);
    //处理动态文件的函数
    int ProcessCGI(Context* context);
};

}//end http_server,用来说明命名空间的结束位置
